package com.segment.dcore.dubbo.filter.service;

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.Result;
import com.alibaba.dubbo.rpc.RpcContext;
import com.segment.dcore.base.constants.FilterOrderConstants;
import com.segment.dcore.base.constants.ServiceConstants;
import com.segment.dcore.base.filter.InvokeContext;
import com.segment.dcore.dubbo.enums.InvokeEnum;
import com.segment.dcore.dubbo.filter.AbstractDubboServiceFilter;
import com.segment.dcore.dubbo.filter.context.DubboInvokeContext;
import com.segment.dcore.dubbo.model.DubboTraceLog;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

/**
 * 服务访问日志拦截器
 *
 * Created by huapeng.hhp on 2017/2/27.
 */
@Service("dubboServiceAccessLogFilter")
public class DubboServiceAccessLogFilter extends AbstractDubboServiceFilter {
	/** LOGGER for ServiceInvokeFilter */
	private static final Logger LOGGER = LoggerFactory.getLogger(DubboServiceAccessLogFilter.class);
	/** 消费者 */
	private static final Logger LOGGER_CONSUMER = LoggerFactory.getLogger(ServiceConstants.TR_CONSUMER_LOG);
	/** 生产者 */
	private static final Logger LOGGER_PROVIDER = LoggerFactory.getLogger(ServiceConstants.TR_PROVIDER_LOG);

	@Override
	public int getOrder() {
		return FilterOrderConstants.ACCESS_LOG_FILTER_ORDER;
	}

	@Override
	protected void before(InvokeContext invokeContext) {
		invokeContext.setStartTime(System.currentTimeMillis());
	}

	@Override
	protected void after(InvokeContext invokeContext) {
		invokeContext.setEndTime(System.currentTimeMillis());
		writeAccessLog((DubboInvokeContext) invokeContext);
	}

	/**
	 * 写入日志
	 * 
	 * @param invokeContext
	 */
	private void writeAccessLog(DubboInvokeContext invokeContext) {
		Invoker<?> invoker = invokeContext.getInvoker();
		Invocation inv = invokeContext.getInv();
		try {
			DubboTraceLog traceLog = new DubboTraceLog();
			RpcContext context = RpcContext.getContext();
			Class<?> interfaceClass = invoker.getInterface();
			String serviceName;
			if (interfaceClass == null) {
				serviceName = "";
			} else {
				serviceName = interfaceClass.getName();
			}
			String appName = invoker.getUrl().getParameter(Constants.APPLICATION_KEY);
			String version = invoker.getUrl().getParameter(Constants.VERSION_KEY);
			traceLog.setAppName(appName);
			// 调用链路入口名称
			String entryName = inv.getAttachment(ServiceConstants.INSIGHT_ENTRY);
			if (entryName == null) {
				entryName = "";
			}
			traceLog.setEntryName(entryName);
			// 深度
			traceLog.setDepth(Integer.parseInt(inv.getAttachment(ServiceConstants.INSIGHT_DEPTH)));
			// 顺序
			traceLog.setOrder(Integer.parseInt(inv.getAttachment(ServiceConstants.INSIGHT_ORDER)));
			// 调用链路ID
			traceLog.setContextId(inv.getAttachment(ServiceConstants.INSIGHT_REQUEST_ID));
			// 当前时间 ms为单位
			traceLog.setCurrentTimeMillis(System.currentTimeMillis());
			// 调用者关系
			traceLog.setInvokeRemoteHost(context.getRemoteHost());
			traceLog.setInvokeRemotePort(context.getRemotePort());
			traceLog.setInvokeLocalHost(context.getLocalHost());
			traceLog.setInvokeLocalPort(context.getLocalPort());
			// 服务名.方法名,要根据serviceName区分两种情况
			if (serviceName.startsWith("com.alibaba.dubbo.rpc.service.GenericService")) {
				traceLog.setInvokeMethod(invoker.getUrl().getParameter("interface") + "." + inv.getArguments()[0]);
			} else {
				traceLog.setInvokeMethod(serviceName + "." + inv.getMethodName());
			}
			// 版本号
			traceLog.setVersion(version);
			// 调用结果
			Result result = invokeContext.getResult();
			if (result == null) {
				traceLog.setInvokeResult("false");
				traceLog.setSuccess(false);
			} else {
				traceLog.setInvokeResult("" + !result.hasException());
				if (result.hasException()) {
					traceLog.setSuccess(false);
				} else {
					traceLog.setSuccess(true);
				}
			}
			// 设置入参
			traceLog.setParams(inv.getArguments());
			// 响应时间
			long startTime = invokeContext.getStartTime();
			long endTime = invokeContext.getEndTime();
			traceLog.setResponseTime(endTime - startTime);
			traceLog.setInvokeChannel(invokeContext.getInvokeEnum().name());
			if (invokeContext.getInvokeEnum() == InvokeEnum.DUBBO_PROVIDER) {
				LOGGER_PROVIDER.info(traceLog.toString());
			} else if (invokeContext.getInvokeEnum() == InvokeEnum.DUBBO_CONSUMER) {
				// 消费者调用日志
				LOGGER_CONSUMER.info(traceLog.toString());
			} else {
				// 未知来源的调用
				LOGGER.warn("未知来源的调用， 请关注");
				return;
			}
		} catch (Throwable t) {
			LOGGER.warn("Exception in ServiceAccessLogFilter of service(" + invoker + " -> " + inv + ")", t);
		}
	}
}
